/*
 * main.c
 *
 *  Created on: 2023-03-13
 *      Author: chenss
 */

#include "main.h"
#include "NuClock/ClockConfig.h"
#include "NuPin/PinConfig.h"
#include "utils.h"
#include "message_queue.h"
#include "ctimer.h"
#include "hw.h"
#include "tick.h"
#include "serial.h"
#include "mstdio.h"
#include "shell.h"
#include "watch_dog.h"
#include "version.h"
#include "CmBacktrace/cm_backtrace.h"
#include "motor.h"

static int _MsgDeliver(void *arg);

static Ctimer_t _runTimer;
static bool _runLedStop;
const char *_model = MODEL;
const char *_softwareVersion = VERSION;
const char *_buildTime = BUILD;
const char *_config = CONFIG;

static int _OnRun(void *argument)
{
    UNUSED(argument);

#ifndef CMSIS_OS
    if(! _runLedStop)
    {
        HW_Toggle(HW_Out_Run);
    }
#endif

    return 0;
}

void SYS_Init(void)
{
    ClockConfig_init();
    PinConfig_init();

    NVIC_SetPriorityGrouping(NVIC_PRIORITY_GROUP);
}

int main()
{
    _runLedStop = false;
    SYS_Init();

    MsgQueue_Init(_MsgDeliver);
//    BackupSRAM_Init();
    Ctimer_Init();
    HW_Init();
    Tick_Init();
    Serial_Init();
    MStdio_Init();
    cm_backtrace_init(_model, "V0.1", _softwareVersion);
    Shell_Init();
    Motor_Init();

    ON_RELEASE(WatchDog_Init();)
    Ctimer_Start(&_runTimer, CTIMER_TICK_SECOND(1), _OnRun, 0, NULL);
    printf("\n%s\nVersion: %s\nBuild  : %s\n", _model, _softwareVersion, _buildTime);

//    Serial_TestCmd(Serial_0, TRUE);
    while(1)
    {
        MsgQueue_Service();
        Ctimer_Service();
        IDLE();
    }

    return -1;
}

void assert_failed(const char *file, uint32_t line)
{
    nbprintf("Assert Failed: %s:%lu\n", file, line);
    while(1);
}

void HAL_Delay(uint32_t ms)
{
    ETimer_t t;

    ETimer_Set(&t, ms);
    while(! ETimer_Expired(&t));
}

void Main_StopRunLed(void)
{
    _runLedStop = true;
    HW_Config(HW_Out_Run, HW_State_Inactive);
}

/**
 * @brief 处理消息
 * @param msg 消息指针
 */
static int _MsgDeliver(void *arg)
{
    MsgQueue_Msg_t *msg = arg;
//    debug("MQ: Dealing msg id: %d\n", msg->id);

    switch(msg->id)
    {
    case MsgQueue_Id_ShellLineIn:
    {
        Stream_LineSignal_t *sig = (Stream_LineSignal_t *)msg->data;

        Shell_OnLine(sig);
    }
        break;

    case MsgQueue_Id_Callback:
    {
        Callback_Info_t *info;

        info = (Callback_Info_t *)msg->data;
        info->callback(info->arg);
    }
        break;

    case MsgQueue_Id_SerialTest:
        Serial_OnTest(msg);
        break;

    case MsgQueue_Id_Debug:
        Mstdio_OnMPrintf(msg);
        break;

//    case MsgQueue_Id_Jump:
//    {
//        uint32_t addr = *(uint32_t *)msg->data;
//
//        Main_Jump(addr);
//    }
//        break;

    default:
        debug("Error: Unknown Message, id = %d!\n", msg->id);
        break;
    }

    return 0;
}

void Main_SetRunLed(bool isEnable)
{
    if(isEnable)
    {
        if(! Ctimer_IsActive(&_runTimer))
        {
            Ctimer_Start(&_runTimer, CTIMER_TICK_SECOND(1), _OnRun, 0, NULL);
        }
    }
    else
    {
        Ctimer_Stop(&_runTimer);
    }
}

static int _ShellCmdReset(void *stream, int argc, char *argv[])
{
    UNUSED(argc);
    UNUSED(argv);

    chprintf(stream, "System Reset!\r\n");
    Stream_Flush(stream);
    HAL_Delay(5);
//    __disable_fault_irq();
    SYS_UnlockReg();
    SYS_ResetChip();
    SYS_LockReg();
    return 0;
}

SHELL_CMD(reset, _ShellCmdReset, "Reset the MCU", "");

static int _ShellCmdVersion(void *stream, int argc, char *argv[])
{
    UNUSED(argc);
    UNUSED(argv);

    chprintf(stream, "Version: %s\nBuild  : %s\nConfig : %s\n", _softwareVersion, _buildTime, _config);
    return 0;
}

SHELL_CMD(version, _ShellCmdVersion, "Shows software version.", "");

static int _ShellCmdUptime(void *stream, int argc, char *argv[])
{
    UNUSED(argc);
    UNUSED(argv);
    uint32_t uptime = (uint32_t)(Tick_Milliseconds() / 1000);
    unsigned int hours, minutes, seconds;

    hours = uptime / (60 * 60);
    minutes = (uptime / 60) % 60;
    seconds = uptime % 60;
    chprintf(stream, "up %02u:%02u:%02u\n", hours, minutes, seconds);
    chprintf(stream, "microseconds: %lu\n", (unsigned long)Tick_Microseconds());

    return 0;
}

SHELL_CMD(uptime, _ShellCmdUptime, "Shows how long the system has been running.", "");
